#include "client.h"
extern char sig_in[60];
extern pthread_mutex_t pmt;
static int talk_ii;  //用于计算本地数据表中的数据
extern int sockfd;
extern MISC *misc;
extern struct bro bro_list[20];
extern char RET[20];


// 将INFO结构体打包成json
/* 
typedef struct {
    int fd;
    unsigned long cmd;
    char uid[UID_LENTGTH];
    char passwd[PWD_LENGTH];
    char msg[MSG_LENGTH];
} INFO;
*/
void info_To_Json (INFO *info, char *buf)
{
    // 1>将结构体id转化为json小对象id;
    struct json_object *obj_fd = json_object_new_int(info->fd);
    struct json_object *obj_cmd = json_object_new_int(info->cmd);
    struct json_object *obj_uid = json_object_new_string(info->uid);
    struct json_object *obj_passwd = json_object_new_string(info->passwd);
    struct json_object *obj_msg = json_object_new_string(info->msg);

    //创建大结构体
    struct json_object *object = json_object_new_object();

    //将小结构体打包到大结构体中
    json_object_object_add(object, "fd", obj_fd);
    json_object_object_add(object, "cmd", obj_cmd);
    json_object_object_add(object, "uid", obj_uid);
    json_object_object_add(object, "passwd", obj_passwd);
    json_object_object_add(object, "msg", obj_msg);
    
    //最后将大结构体转化成字符串，并拷贝到指定地址
    //先清空指定字符串中的内容
    bzero(buf, sizeof(buf));
    //再进行copy
    strcpy(buf, json_object_to_json_string(object));
}
//
void json_To_Info(char *p, INFO *info) 
{
    // 解包结构体数�?
    // 1. 将字符串转成json大对�?
    struct json_object *object = json_tokener_parse(p);
    // 2. 根据标签将json大对象解包成json小对�?
    struct json_object *obj_fd = json_object_object_get(object, "fd");
    struct json_object *obj_cmd = json_object_object_get(object, "cmd");
    struct json_object *obj_uid = json_object_object_get(object, "uid");
    struct json_object *obj_passwd = json_object_object_get(object, "passwd");
    struct json_object *obj_msg = json_object_object_get(object, "msg");
    // 3. 将json小对象转换成结构体对�?
    info->fd = json_object_get_int(obj_fd);
    info->cmd = json_object_get_int(obj_cmd);
    strcpy(info->uid, json_object_get_string(obj_uid));
    strcpy(info->passwd, json_object_get_string(obj_passwd));
    strcpy(info->msg, json_object_get_string(obj_msg));
}
//客户端在需要和服务器进行数据传输时才会考虑连接到服务器，但是连接只需要一次即可，顾需要封装一个函数，根据静态变量的值来判断客户端和服务器是否连接
//如果没有连接，则尝试连接，如果已经连接，则忽略
void connect_To_Server(void)
{
    static int status = 0;
    if (status == 0) {
        //当状态为false时，尝试连接服务器
        if (connect(misc->sockfd, (struct sockaddr *)&misc->server, sizeof(misc->server)) < 0) {
            perror("connect");
        }
        status = 1;
    }

}

// 将struct user结构体打包成json
void info_json(void *info, char *buf) 
{
  struct user *user_info = (struct user *)info;
  // 将注册的账号密码json打包
  // 1>将结构体id转化为json小对象id;
  struct json_object *obj_uid = json_object_new_string(user_info->uid);
  struct json_object *obj_passwd = json_object_new_string(user_info->passwd);

  // 2>创建json大对象(大箱子=object)
  struct json_object *object = json_object_new_object();

  // 3>将json小对象放入大对象中，并给每个小对象打上标签key
  json_object_object_add(object, "uid", obj_uid);
  json_object_object_add(object, "passwd", obj_passwd);

  // 4>将json大对象转化为字符串
  strcpy(buf, json_object_to_json_string(object));
  //*buf = json_object_to_json_string(object);
}

/* 描述：通过文件描述符，给服务器发送信号
 * 参数：fd 文件描述符； sig 信号
 * 
*/
void sig_send(int fd, char *sig) 
{ 
    char buf[50];
    bzero(buf, sizeof(buf));
    strcpy(buf, sig);
    int ret = send(fd, buf, strlen(buf), 0);
    if (ret < 0) {
        perror("send");
        return;
    }
}

// 判断字符串arr是否置0, 如果非0就会返回0， 置0返回1
int is_zero(char *arr, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        if (arr[i] != 0)
            return 0;
    }
    return 1;
}
/*
//线程: 用于处理ctrl+c信号
void * client_ctrlc(void *arg)
{
        signal(2, send_exit);
}
*/



// struct bro结构体数组解包（多个好友的信息--uid和status）
// struct bro brolist[20] = {0};
// char buf[200] = {0};
// info_rejson_array(brolist, buf, &len);
void info_rejson_array(void *info, char *p, int *len) {
  // p ---> 传进来的json打包好的字符串的地址
  // info 传进来的结构体数组（地址传送）
  // 解包
  // 解包过程
  // 循环前先指定名称
  // 1>将字符串转化为数组对象
  struct json_object *obj_arrs = json_tokener_parse(p);

  // 2>获取数组对象中 大对象的个数
  *len = json_object_array_length(obj_arrs);
  struct bro *brolist = (struct bro *)info;

  // 3>解包操作不需要一个数据写一条，而是交给循环
  // 循环前先指定名称
  int i; // 循环的中间条件
  // 待定
  struct json_object *objects;    // 大对象
  struct json_object *obj_uid;    // uid 的json小对象名称
  struct json_object *obj_status; // status的json小对象名称

  for (i = 0; i < *len; i++) {
    // 1>从数组对象中获取大对象
    objects = json_object_array_get_idx(obj_arrs, i);

    // 2>通过标签，从大对象中获取json小对象
    struct json_object *obj_uid = json_object_object_get(objects, "uid");
    struct json_object *obj_status = json_object_object_get(objects, "status");

    // 3>解析小包，并打印，得到uid 和 status的具体数据
    strcpy(brolist[i].uid, json_object_get_string(obj_uid));
    brolist[i].status = json_object_get_int(obj_status);
  }
}

// struct msg结构体数组解包（好友多条聊天信息--status和message）
// struct msg msglist[20] = {0};
// char buf[200] = {0};
// msg_rejson_array(msglist, buf, &len);
void json_msg(void *info, char *p, int *len)
{
    // p ---> 传进来的json打包好的字符串的地址
    // info 传进来的结构体数组（地址传送）
    // 解包
    // 解包过程
    // 循环前先指定名称
    // 1>将字符串转化为数组对象
    struct json_object *obj_arrs = json_tokener_parse(p);

    // 2>获取数组对象中 大对象的个数
    *len = json_object_array_length(obj_arrs);
    struct msg *msglist = (struct msg *)info;

    // 3>解包操作不需要一个数据写一条，而是交给循环
    // 循环前先指定名称
    int i; // 循环的中间条件
    // 待定
    struct json_object *objects;     // 大对象
    struct json_object *obj_message; // message的json小对象名称

    for (i = 0; i < *len; i++) {
        // 1>从数组对象中获取大对象
        objects = json_object_array_get_idx(obj_arrs, i);

        // 2>通过标签，从大对象中获取json小对象
        struct json_object *obj_message = json_object_object_get(objects, "message");

        // 3>解析小包，并打印，得到uid 的具体数据
        strcpy(msglist[i].msg, json_object_get_string(obj_message));
    }
}

// 接收信息线程
void *recvinfo(void *arg) 
{
    int fd = *((int *)arg);
    char buf[100];
    while (1) {
        bzero(buf, sizeof(buf));
        recv(fd, buf, sizeof(buf), 0);
        // 除了打印还可以做其他操作，待补充
        // 待补充

        if (!strcmp(buf, "$$")) {
            printf("通道关闭!\n");
            pthread_exit(NULL); // 进程死亡
        }                     // 接收到·
        printf("%s\n", buf);
    }
    // signal(SIGINT, end_sig);
}

// 发送信息线程
void *sendinfo(void *arg) 
{
    int fd = *((int *)arg);
    char buf[100];
    while (1) {
        bzero(buf, sizeof(buf));
        scanf("%s", buf);
        send(fd, buf, strlen(buf), 0);
        if (!strcmp(buf, "$$")) { // 私聊通道关闭
            pthread_exit(NULL);
        }
    }
}

void exit_pthread(int arg)
{
    pthread_exit(NULL);
}

void pop_up(char *info, int time) 
{
    char tip[30] = {0};
    strcpy(tip, info);
    char *p = tip;
    int num = 0; // 计算中文字符数
    while (*p != '\0') {
        if (*p < 0 && (*(p + 1) < 0 || *(p + 1) > 63)) { // 识别中文字符
            p += 3;
            num++;
        } else {
            p++;
        }
    }
    int len = strlen(tip) - num;
    int offset = (20 - len) / 2;
    if ((!strcmp(tip, "登录中")) || (!strcmp(tip, "程序退出中"))) {
        offset -= 2;
        time = 0;
    }
    printf(LIGHT_BLUE);
    printf("\033[16;61H ======================\n");
    printf("\033[17;61H｜                    ｜\n");
    printf("\033[18;61H｜                    ｜\n");
    printf("\033[18;%dH%s", 63 + offset, tip);
    printf("\033[19;61H｜                    ｜\n");
    printf("\033[20;61H ======================\n");
    printf("\033[30;66H");  //定位到底部状态栏，弹窗仅用于展示，不做交互，故不需要定位光标
    sleep(time);
}

void alt_pop_up(char *info, void *pop_info) // 传参进来的info
{
    struct user *uid_info = (struct user *)pop_info;
    //jstruct user uid_info = {0}; // 保存你输入的uid
    char tip[30] = {0};
    strcpy(tip, info);
    char *p = tip;
    int num = 0; // 计算中文字符数
    while (*p != '\0') {
        if (*p < 0 && (*(p + 1) < 0 || *(p + 1) > 63)) { // 识别中文字符
            p += 3;
            num++;
        } else {
            p++;
        }
    }
    int len = strlen(tip) - num;
    int offset = (20 - len) / 2;
    printf(LIGHT_BLUE);
    printf("\033[16;61H ======================\n");
    printf("\033[17;61H｜                    ｜\n");
    printf("\033[18;61H｜  ｜            ｜  ｜\n");
    printf("\033[17;%dH%s", 63 + offset, tip);
    printf("\033[19;61H｜   --------------   ｜\n");
    printf("\033[20;61H ======================\n");
    do{
        printf("\033[18;68H");
        bzero(uid_info->uid, sizeof(uid_info->uid));
        scanf("%s", uid_info->uid);
    }while(is_zero(uid_info->uid, 50));//防止输入为空
    printf("\033[30;66H");
}

/*int printf_message(void *arg,int column, char **data, char **name){

        char buf[110];
        bzero(buf, sizeof(buf));
        printf("033[%d;1H%s", talk_ii, data[1]);
        talk_ii++;

        return 0;
}*/

int printf_message(void *arg, int column, char **data, char **name) {
  pthread_mutex_t pmt;
  pthread_mutex_lock(&pmt);
  char buf[70];
  bzero(buf, sizeof(buf));
  int i;
  if (talk_ii <= 22) {
    printf("033[%d;1H%s", talk_ii, data[1]);
    talk_ii++;
    // printf("\033[K");
    // printf("\033[36;1H\033[K");
    // printf("\033[K");
  }
  if (talk_ii > 22) { // 满了并且打印了
    // 先将光标所在行
    // 将23行至26行全部清空
    printf("\033[23;1H\033[K");
    printf("\033[24;1H\033[K");
    printf("\033[25;1H\033[K");
    printf("\033[26;1H\033[K");
    printf("\033[26;5H\n\n\n\n\n"); // 在最后一行顶上去
    printf("\033[23;1H");
    for (i = 0; i < 70; i++) {
      printf("-");
    }
    talk_ii = talk_ii - 5;
    printf("\033[25;3H> : ");
    printf("033[%d;1H%s", talk_ii, data[1]);
    talk_ii++;
  }
  pthread_mutex_unlock(&pmt);
  return 0;
}

void *get_message(void *arg) {
  char uid[25] = "_";
  bzero(uid, sizeof(uid));
  strcpy(uid, (char *)arg);
  sqlite3 *ppdb;
  char *errmsg;
  sqlite3_open("2.db", &ppdb);
  // 查询表并进行对比
  // 1. 查询语句
  char sql[200];
  bzero(sql, sizeof(sql));
  while (1) {
    pthread_mutex_t pmt;
    pthread_mutex_lock(&pmt);
    sprintf(sql, "select *from %s where status == 1;", uid);
    int ret = sqlite3_exec(ppdb, sql, printf_message, arg, &errmsg);

    if (ret != SQLITE_OK) { // 回调函数异常
      // 不存在fd		//sig_send(fd, "sign error");
      // //数据库中存在同名账号，发送注册失败信号
      sqlite3_close(ppdb); // 关闭数据库
      return NULL;
    }
    char buf[100];
    /*while(1){
            bzero(buf, sizeof(buf));
            scanf("%s", buf);
            send(fd, buf, strlen(buf), 0);
            if(!strcmp(buf, "$$")){   //私聊通道关闭
                    pthread_exit(NULL);
            }
    }*/
    pthread_mutex_unlock(&pmt);
  }
}

//回调函数：用于将聊天表中最后22条数据保存进链队中
int print_message(void *info, int column, char **date, char **name)
{
    talk_ii++;
    struct msg_que *msg_info = (struct msg_que *)info;
    if(talk_ii <= 22){  //当聊天记录表中的信息小于22条时，只入队，不出队
        //创建全局链队结构体(uid, message)，队列大小30，保留表中最后30条数据
        struct msg_node *node = (struct msg_node *)malloc(sizeof(struct msg_node));
        bzero(node->uid, sizeof(node->uid));
        bzero(node->msg, sizeof(node->msg));
        strcpy(node->uid, date[1]); //将uid信息保存进去
        strcpy(node->msg, date[2]);
        //节点入队
        node->next = msg_info->tail->next;
        msg_info->tail->next = node;
        msg_info->tail = node;
    }else{  //当表中数据超过22条时，入队一条数据，出队一条数据
        struct msg_node *node = (struct msg_node *)malloc(sizeof(struct msg_node));
        bzero(node->uid, sizeof(node->uid));
        bzero(node->msg, sizeof(node->msg));
        strcpy(node->uid, date[1]);
        strcpy(node->msg, date[2]);
        //入队一条数据
        node->next = msg_info->tail->next;
        msg_info->tail = node;
        //出队一条数据
        struct msg_node *temp = msg_info->head->next;
        msg_info->head->next = temp->next;
        temp->next = NULL;
        free(temp);
    }
    return 0;

}

void *pri_msg(void *arg)
{
    struct info *uid_info = (struct info *)arg;

    sqlite3 *ppdb;
    char *errmsg;
    //打开数据库
    sqlite3_open("msg.db", &ppdb);
    //确定本地聊天表名
    char table_name[50] = "_";
    strcat(table_name, uid_info->b_uid);
    //创建一个链队
    struct msg_que head;
    //链队初始化
    struct msg_node *temp = (struct msg_node *)malloc(sizeof(struct msg_node));
    bzero(temp->uid, sizeof(temp->uid));
    head.head = temp;
    head.tail = temp;
    //刷新打印本地表中的信息
    while (1) {
        //聊天界面
        system("clear");
        system("resize -s 26 70;"); // resize [-cu][-s <行数> <列数>]
        system("clear");
        printf("\033[23;1H"); // 在第23行打印 ------
        int i;
        for (i = 0; i < 70; i++) {
            printf("-");
        }
        //每次刷新打印需要将全局变量置于0
        talk_ii = 0;
        char sql[200] = {0};
        sprintf(sql, "select * from '%s';", table_name);
        int ret = sqlite3_exec(ppdb, sql, print_message, &head, &errmsg);
        if(ret != SQLITE_OK){
            printf("error: %s\n", errmsg);
            return NULL;
        }
        //打印队列中talk_ii, talk_ii可能等于聊天表中的数据，也可能大于聊天表中的数据，但不管怎样，一定大于等于链队节点个数
        //而链队中的节点一定是按打印顺序排列的
        for(i = 0; i < talk_ii; i++){
            if(head.head->next == head.tail){  //队列中只剩最后一个元素
                head.tail = head.head;
            }
            struct msg_node *ff = head.head->next;
            if(!strcmp(ff->uid, uid_info->m_uid)){  //出队一个数据，打印一个数据
                //打印我的数据
                printf("\033[%d;1H%70s", talk_ii, ff->msg);
                printf("\033[25;3H> : ");
                //出队
                head.head->next = ff->next;
                ff->next = NULL;
                free(ff);
            }else if(!strcmp(ff->uid, uid_info->b_uid)){
                //打印好友的数据
                printf("\033[%d;1H%s", talk_ii, ff->msg);
                printf("\033[25;3H> : ");
                head.head->next = ff->next;
                ff->next = NULL;
                free(ff);
            }else{ //没有数据，就把光标定位到输入输出处
                printf("\033[25;3H> : ");
            }
        }
    }
    signal(SIGINT, exit_pthread);
}

// 用户端接收和发送信息
void talk_to(void *info, char *uid)
{
    struct user *my_info = (struct user *)info;
    sig_send(my_info->fd, "talkto");

    struct user bro_info = {0};
    strcpy(bro_info.uid, uid);

    // 将结构体数据打包发送
    char buf[100];
    info_json(&bro_info, buf);

    // 发送打包数据
    send(my_info->fd, buf, strlen(buf), 0);

    //将自己和好友的uid保存
    struct info uid_info = {0};
    strcpy(uid_info.m_uid, my_info->uid);
    strcpy(uid_info.b_uid, bro_info.uid);
    // 开一个线程, 只要还停留在聊天界面，就打印信息
    pthread_t Primsg;
    pthread_create(&Primsg, NULL, pri_msg, &uid_info);
    // 服务器反馈客户端，将之前对方发给自己的未接收信息发过来
    // 解包聊天信息，并存入客户端数据库中----一个线程---专门处理来自服务器的json聊天信息包，并解包写进数据库中
    // 发送消息
    // 展示界面
    system("clear");
    system("resize -s 26 70;"); // resize [-cu][-s <行数> <列数>]
    system("clear");
    printf("\033[23;1H"); // 在第23行打印 ------
    int i;
    for (i = 0; i < 70; i++) {
        printf("-");
    }

    // 将自己输入的内容插入数据库中，发送
    while (1) {
        pthread_mutex_t pmt;
        pthread_mutex_lock(&pmt);
        printf("\033[25;3H> : ");
        // char aa[110];
        bzero(buf, sizeof(buf));
        scanf("%s", buf); // 最多输入一行，输入多了有bug
        //如果输入为"$$"，则结束通话
        if (!strcmp(buf, "$$")) { // 私聊通道关闭
            pthread_kill(Primsg, SIGINT); //给线程发送给SIGINT信号
            return;
        }
        //将输入数据插入数据库的聊天表中
        sqlite3 *ppdb;
        char *errmsg;
        sqlite3_open("msg.db", &ppdb); // 打开数据库
        // 1. 确定聊天表的名称
        char table_name[60] = "_";
        strcat(table_name, bro_info.uid); // 存储表名
        char sql[500];
        bzero(sql, sizeof(sql));
        sprintf(sql, "insert into '%s' values(1, '%s', '%s');", table_name, my_info->uid, buf);
        int ret = sqlite3_exec(ppdb, sql, NULL, NULL, &errmsg);
        if (ret != SQLITE_OK) {
            printf("error: %s\n", errmsg);
            return ;
        }
        // 关闭数据库
        sqlite3_close(ppdb);
        //发送数据
        send(misc->sockfd, buf, strlen(buf), 0);
    }
}

int sign_in(void *info, char *uid)
{
    struct user *my_info = (struct user *)info;
    sig_send(my_info->fd, "signin");

    strcpy(my_info->uid, uid);
    printf("\033[20;62H"); // 定位到下面用于接收输入的密码
    scanf("%s", my_info->passwd);

    char buf[100];
    // 打包结构体
    info_json(my_info, buf);

    // 发送json包
    send(my_info->fd, buf, strlen(buf), 0);

    //发送完，清空my_info中保存的信息, 但不清除fd，方便下次保存新的数据
    bzero(my_info->uid, sizeof(my_info->uid));
    bzero(my_info->passwd, sizeof(my_info->passwd));

    return 0;
}

int log_in(void *info, char *uid)
{
    struct user *my_info = (struct user *)info;
    sig_send(my_info->fd, "login");

    char buf[100]; 
    bzero(buf, sizeof(buf)); // 结构体json打包
    info_json(my_info, buf);
    // 发送json包给服务器
    send(my_info->fd, buf, strlen(buf), 0);


    return 0;
}

// 客户端选择退出登录功能时，自动向服务器发送信号的函数
/*
void log_out(int sockfd)
{
    char buf[10];
    sig_send(sockfd, "logout");

}
*/

/*
void sig_exit(int sockfd)
{
    sig_send(sockfd, "exit");
}
*/

// 登录等待返回时界面
void log_wait()
{
    printf(LIGHT_BLUE);
    pop_up("登录中", 1);
    int i;
    int j = 2;
    while (j) {
        fflush(0);
        printf("\033[18;74H");
        // printf("\033[K");  //清空光标位置至行尾所有内容
        for (i = 0; i < 4; i++) {
            printf(".");
            fflush(stdout);
            usleep(400000);
        }
        clear_up(18, 74, 4);
        j--;
    }
    printf(NONE);
}

void clear_up(int row, int column, int num)
{
    // 定位光标
    printf("\033[%d;%dH", row, column);
    // 打印空格
    int i;
    for (i = 0; i < num; i++) {
        printf(" ");
    }
}

void page_frame()
{
    system("clear");
    printf("\033[1;34m");
    printf(
      "\033[6;38H "
      "__________________________________________________________________\n");
    printf("\033[7;38H｜                                                         "
         "       ｜\n");
    printf("\033[8;38H｜                                                         "
         "       ｜\n");
    printf("\033[9;38H｜_________________________________________________________"
         "_______｜\n");
    printf("\033[10;38H｜                                                        "
         "        ｜\n");
    printf("\033[11;38H｜                                                        "
         "        ｜\n");
    printf("\033[12;38H｜                                                        "
         "        ｜\n");
    printf("\033[13;38H｜                                                        "
         "        ｜\n");
    printf("\033[14;38H｜                                                        "
         "        ｜\n");
    printf("\033[15;38H｜                                                        "
         "        ｜\n");
    printf("\033[16;38H｜                                                        "
         "        ｜\n");
    printf("\033[17;38H｜                                                        "
         "        ｜\n");
    printf("\033[18;38H｜                                                        "
         "        ｜\n");
    printf("\033[19;38H｜                                                        "
         "        ｜\n");
    printf("\033[20;38H｜                                                        "
         "        ｜\n");
    printf("\033[21;38H｜                                                        "
         "        ｜\n");
    printf("\033[22;38H｜                                                        "
         "        ｜\n");
    printf("\033[23;38H｜                                                        "
         "        ｜\n");
    printf("\033[24;38H｜                                                        "
         "        ｜\n");
    printf("\033[25;38H｜                                                        "
         "        ｜\n");
    printf("\033[26;38H｜                                                        "
         "        ｜\n");
    printf("\033[27;38H｜                                                        "
         "        ｜\n");
    printf("\033[28;38H｜________________________________________________________"
         "________｜\n");
    printf("\033[29;38H｜                                                        "
         "        ｜\n");
    printf("\033[30;38H｜                                                        "
         "        ｜\n"); // 功能选择输入口
    printf("\033[31;38H｜________________________________________________________"
         "________｜\n");
}

void init_page() {
  page_frame();
  printf("\033[1;35m");
  printf(
      "\033[8;40H                       ikun聊天室--战损版                   ");
  printf("\033[1;36m");
  printf(
      "\033[12;43H    ==================================================   ");
  printf(
      "\033[13;43H   ｜                                                ｜  ");
  printf(
      "\033[14;43H   ｜                                                ｜  ");
  printf(
      "\033[15;43H   ｜                   ___________________________  ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[16;43H   ｜    _______   ID号｜                         ｜ ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[17;43H   ｜  ｜       ｜      ===========================  ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[18;43H   ｜  ｜(^*.*^)｜                                   ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[19;43H   ｜  ｜   V   ｜      ___________________________  ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[20;43H   ｜   ¯¯¯¯¯¯¯¯¯  密码｜                         ｜ ｜  ");
  // usleep(LOGSLEEP);
  printf(
      "\033[21;43H   ｜                   ===========================  ｜  ");
  printf(
      "\033[22;43H   ｜                                                ｜  ");
  printf(
      "\033[23;43H   ｜                                                ｜  ");
  printf(
      "\033[24;43H    ==================================================   ");
  printf("\033[1;31m");
  printf("\033[25;43H      Exit(e)                             Sign in(s)    "
         " "); // 功能选择输入口
  printf("\033[1;33m");
  printf(
      "\033[30;45H            真诚地邀请您加入我们ikun大家庭             "); // 功能选择输入口
  printf("\033[0m");
}

void sign_page() {
  page_frame();
  printf("\033[1;35m");
  printf("\033[8;40H                        欢迎注册ikun账号             ");
  printf("\033[1;36m");
  printf("\033[12;47H    ============================================   ");
  printf("\033[13;47H   ｜                                          ｜  ");
  printf("\033[14;47H   ｜                                          ｜  ");
  printf("\033[15;47H   ｜         ____________________________     ｜  ");
  // usleep(LOGSLEEP);

  printf("\033[16;47H   ｜    ID号｜                          ｜    ｜  ");
  // usleep(LOGSLEEP);
  printf("\033[17;47H   ｜         ============================     ｜  ");
  // usleep(LOGSLEEP);
  printf("\033[18;47H   ｜                                          ｜  ");
  // usleep(LOGSLEEP)
  printf("\033[19;47H   ｜         ____________________________     ｜  ");
  // usleep(LOGSLEEP);
  printf("\033[20;47H   ｜    密码｜                          ｜    ｜  ");
  // usleep(LOGSLEEP);
  printf("\033[21;47H   ｜         ============================     ｜  ");
  printf("\033[22;47H   ｜                                          ｜  ");
  printf("\033[23;47H   ｜                                          ｜  ");
  printf("\033[24;47H    ============================================   ");
  printf("\033[11;31m");
  printf(
      "\033[25;47H      Exit(e)                         Back(b)     "); // 功能选择输入口
  printf("\033[1;33m");
  printf(
      "\033[30;47H              请输入注册账号 和 密码               "); // 功能选择输入口
  printf("\033[0m");
}

// 登录成功后的界面
void main_page() {
  page_frame();
  printf("\033[7;40H                        ｜                    ｜           "
         "     ");
  printf("\033[8;40H        好友列表        ｜      群聊列表      ｜     "
         "功能区     ");
  printf("\033[9;38H｜________________________｜____________________｜_________"
         "_______｜\n");
  printf("\033[10;40H                        ｜                    ｜          "
         "     ");
  printf("\033[11;40H                        ｜                    ｜          "
         "     ");
  printf("\033[12;40H                        ｜                    ｜          "
         "     ");
  printf("\033[13;40H                        ｜                    ｜          "
         "     ");
  printf("\033[14;40H                        ｜                    ｜   A. "
         "添加好友 ");
  printf("\033[15;40H                        ｜                    ｜          "
         "     ");
  printf("\033[16;40H                        ｜                    ｜   D. "
         "删除好友 ");
  printf("\033[17;40H                        ｜                    ｜          "
         "     ");
  printf("\033[18;40H                        ｜                    ｜   R. "
         "刷新界面 ");
  printf("\033[19;40H                        ｜                    ｜          "
         "     ");
  printf("\033[20;40H                        ｜                    ｜   Q. "
         "退出QQ   ");
  printf("\033[21;40H                        ｜                    ｜          "
         "     ");
  printf("\033[22;40H                        ｜                    ｜          "
         "     ");
  printf("\033[23;40H                        ｜                    ｜          "
         "     ");
  printf("\033[24;40H                        ｜                    ｜          "
         "     ");
  printf("\033[25;40H                        ｜                    ｜          "
         "     ");
  printf("\033[26;40H                        ｜                    ｜          "
         "     ");
  printf("\033[27;40H                        ｜                    ｜          "
         "     ");
  printf("\033[28;38H｜________________________｜____________________｜________"
         "________｜\n");
  printf("\033[30;39H   请输入你选择的功能代号：                               "
         "     "); // 功能选择输入口
}

// 接收用户账号和密码信息---可以拓展为接收结构体数据
void info_recv(int fd, void *arg)
{
    struct user *info = (struct user *)arg;
    char buf[100];
    bzero(buf, sizeof(buf));
    int ret = recv(fd, buf, sizeof(buf), 0);
    if (ret < 0) {
        perror("recv");
        return;
    }
    json_user(buf, info); // 解包数据
}

// 将接收的用户数据: 账号和密码信息 json解包---可以拓展为解包结构体数据
void json_user(char *p, void *info)
{
    // 解包结构体数据
    // 1. 将字符串转成json大对象
    struct json_object *object = json_tokener_parse(p);
    // 2. 根据标签将json大对象解包成json小对象
    struct json_object *obj_uid = json_object_object_get(object, "uid");
    struct json_object *obj_passwd = json_object_object_get(object, "passwd");
    // 3. 将json小对象转换成结构体对象
    struct user *log_info = (struct user *)info;
    strcpy(log_info->uid, json_object_get_string(obj_uid));
    strcpy(log_info->passwd, json_object_get_string(obj_passwd));
}

// 线程函数，用于接收和处理其他客户端发来的信号
void *passive(void *arg)
{
    int sockfd = *((int *)arg);
    char buf[512];

    while (1) {
        // re_fresh(sockfd, bro_list);
        bzero(buf, sizeof(buf));
        printf("passive sockfd = %d\n", sockfd);
        printf("Enter passive \n");
        int ret = recv(misc->sockfd, buf, sizeof(buf), 0); // 阻塞接收信号
        printf("client recv buf = %s\n", buf);
        if (ret < 0) {
            syslog(LOG_ERR, "recv sig error");
            // perror("recv");
        }
        printf("passive buf = %s\n", buf);
        //阻塞等待，只要不阻塞了，就说明有信号了
        misc->sig = 1; //说明来信号了
        // re_fresh(sockfd, bro_list);
        // printf("%s\n", buf);
        //
        if (!strcmp(buf, "log ok")) {
            // 登录等待界面
            log_wait();
            //弹窗大小相同，登录成功会覆盖等待登录弹窗
            pop_up("登陆成功!", 1);
            // re_fresh(sockfd, bro_list); // 登录成功需要刷新界面
            bzero(RET, sizeof(RET));
            strcpy(RET, "logok");
        } else if (!strcmp(buf, "log error")) {
            // 登录等待界面
            log_wait();
            pop_up("登录失败，请重试!", 1);
            bzero(RET, sizeof(RET));
            strcpy(RET, "logerror");
        } else if (!strcmp(buf, "sign ok")) {
            pop_up("注册成功, 请登录！", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "signok");
        } else if (!strcmp(buf, "sign error")) { // 注册失败，
            pop_up("该账号已存在！", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "signerror");
        } else if (!strcmp(buf, "add error")) {
            pop_up("该用户不存在！", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "adderror");
        } else if (!strcmp(buf, "add error1")) {
            pop_up("该用户已是您的好友", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "adderror1");
        } else if (!strcmp(buf, "add ok")) {
            pop_up("已发送，等待通过", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "addok");
        } else if(!strcmp(buf, "add error2")){
            pop_up("无法添加自己为好友!", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "adderror2");
        } else if (!strcmp(buf, "bro add")) { // 循环接收buf，当buf=broadd时，接收添加请求
            struct user bro_info = {0};
            //bzero(&bro_info, sizeof(bro_info));
            info_recv(sockfd, &bro_info);
            bzero(buf, sizeof(buf));
            strcpy(buf, bro_info.uid);
            strcat(buf, "添加好友Y/N");
            printf("%s\n", buf);
            /*
                char user[100];
                bzero(user, sizeof(user));
                recv(sockfd,user,strlen(user),0);
                strcat(user, "请求添加好友Y/N");
            */
            // 或者使用交互式弹窗，直接在弹窗中输入yes/no
            //pop_up(buf, 5);
            struct user info;
            //里面的scanf可能会和主程序中的scanf()冲突
            while (1) {
                alt_pop_up(buf, &info); //交互式弹窗的局限在于，仅用于输入账号信息
                //printf("\033[30;66H");  //定位到底部状态栏，弹窗仅用于展示，不做交互，故不需要定位光标
                if (!strcmp(info.uid, "Y")) {
                    sig_send(sockfd, "agreeadd"); // 同意添加信号
                    // 打包发送用户好友uid
                    // recv(sockfd, buf, sizeof(buf), 0);
                    bzero(buf, sizeof(buf));
                    info_json(&bro_info, buf);
                    // 发送好友uid
                    send(sockfd, buf, strlen(buf), 0);
                    //同意添加后，需要刷新好友列表，同时清除弹窗信息
                    re_fresh(sockfd, bro_list);
                    break;
                } else if (!strcmp(info.uid, "N")) {
                    re_fresh(sockfd, bro_list);
                    break;
                }
            }
        } else if (!strcmp(buf, "del ok")) {
            pop_up("删除成功", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "delok");
        } else if (!strcmp(buf, "del error")) {
            pop_up("删除失败, 用户非好友", 2);
            bzero(RET, sizeof(RET));
            strcpy(RET, "delerror");
        } else if(!strcmp(buf, "sys msg")){
            //接收服务器传来的json数据
            char buff[1000]; // 该大小要满足好友上限的数据大小
            //接收好友的uid信息
            struct user bro_info = {0};
            info_recv(sockfd, &bro_info);
            while (1) {
                //接收打包的json聊天数据
                bzero(buff, sizeof(buff));
                recv(sockfd, buff, sizeof(buff), 0);
                struct msg msglist[50] = {0};
                int len;
                json_msg(msglist, buff, &len); // 解包数据

                // 将解包数据放进本地聊天数据库中
                sqlite3 *ppdb;
                char *errmsg;
                sqlite3_open("msg.db", &ppdb); // 打开数据库
                // 查询表并进行对比
                // 1. 查询语句
                char table_name[60] = "_";
                strcat(table_name, bro_info.uid); // 存储表名
                char sql[200];
                int msg_i;
                for (msg_i = 0; msg_i < len; msg_i++) {
                    //把数据插入其中，1代表数据没有读出
                    bzero(sql, sizeof(sql));
                    sprintf(sql, "insert into '%s' values(1, '%s', '%s');", table_name, bro_info.uid, msglist[msg_i].msg);
                    int ret = sqlite3_exec(ppdb, sql, NULL, NULL, &errmsg);
                    if (ret != SQLITE_OK) {
                        printf("error: %s\n", errmsg);
                        return NULL;
                    }
                }
                // 关闭数据库
                sqlite3_close(ppdb);
            }
        }
    }
}


int add_bro(void *info)
{
    struct user *my_info = (struct user *)info;

    // 向服务器发送添加好友信号
    sig_send(my_info->fd, "addbro");

    struct user bro_info;
    //当没有给bro_info.uid赋值时，alt_pop_up一直刷新显示
    alt_pop_up("请输入好友的uid", &bro_info);

    //打包好友信息
    char buf[500] = {0};
    info_json(&bro_info, buf);
    send(my_info->fd, buf, strlen(buf), 0);

    return 0;
}

int del_bro(void *info)
{
    struct user *my_info = (struct user *)info;
    sig_send(my_info->fd, "delbro");
    struct user bro_info;
    alt_pop_up("请输入好友的uid", &bro_info);

    char buf[100] = {0};
    info_json(&bro_info, buf);
    send(my_info->fd, buf, strlen(buf), 0);

    return 0;
}

// 手动刷新好友列表函数，每刷新依次，全局变量结构体里面的数据都刷新一遍，且按在线->离线顺序排列
int re_fresh(int sockfd, void *info) 
{
    struct bro *bro_list = (struct bro *)info;
    bzero(bro_list, sizeof(*bro_list)); // 每次使用，先将结构体清零
    // system("clear"); //屏幕清空
    main_page();                 // page_frame + 列表界面
    sig_send(sockfd, "refresh"); // 更新成功后自动接收好友列表并刷新

    // 先接收服务器端传来的数据信号，若为NULL，则表明无好友数据传来，否则打印好友列表
    char buf[1000] = {0}; // 该大小要满足好友上限的数据大小
    recv(sockfd, buf, sizeof(buf), 0);
    struct bro brolist[20] = {0}; // 用来临时保存好友数据
    if (strcmp(buf, "NULL")) { // 如果接收的字符串不是NULL，则处理该数据
        // 接受来自服务器发送的好友列表
        int len; // 用于保存接收的好友数
        info_rejson_array(brolist, buf, &len);
        // 得到了所有的好友，遍历2次，一次选择在线状态好友和一次选择不在线状态好友
        // 1. 第一次遍历，不重复创建所有好友的本地聊天表
        int tab_i;
        for (tab_i = 0; tab_i < len; tab_i++) {
            // 1. 创建/打开数据库
            sqlite3 *ppdb;
            char *errmsg;
            sqlite3_open("msg.db", &ppdb);
            // 2. 确定表名
            char buf[60] = {0}; // 规定聊天记录表格式： _uid
            strcpy(buf, "_");
            strcat(buf, brolist[tab_i].uid);
            // 3. 创建表
            char sql[500];
            bzero(sql, sizeof(sql));

            // 创建好友聊天信息表 列名==》status int (1为未打印信息，0为已打印信息)
            // 列名==》message char[110]
            sprintf(sql, "create table if not exists '%s'(status int, uid char [50], message char[110]);", buf);
            int ret = sqlite3_exec(ppdb, sql, NULL, NULL, &errmsg);
            if (ret != SQLITE_OK) {
                printf("error: %s\n", errmsg);
                return -1;
            }
            // 4. 关闭数据库
            sqlite3_close(ppdb);
        }

        // 第二次遍历，打印在线好友，并将在线好友依次存入bro_list结构体数组中
        printf("\033[11;42H在线好友▼"); // 设置光标位置
        int brolist_i, brolist_j = 0; // i 循环遍历变量 j 用来统计在线好友总数

        for (brolist_i = 0; brolist_i < len; brolist_i++) {
            if (brolist[brolist_i].status == 1) { // 当好友在线时
                //将在线好友的信息保存到全局结构体变量bro_list中---实时更新该结构体
                bro_list[brolist_i].status = brolist[brolist_i].status;  // 复制状态
                strcpy(bro_list[brolist_i].uid, brolist[brolist_i].uid); // 复制uid
                brolist_j++;  
                printf("\033[%d;42H%d. %s", brolist_j + 11, brolist_j, brolist[brolist_i].uid);
            }
        }

        // 第三次遍历，打印离线好友，并绑定
        printf("\033[%d;42H离线好友▼", brolist_j + 13); // brolist_j+12表示打印离线好友所在的行数
        int brolist_k = 0;
        for (brolist_i = 0; brolist_i < len; brolist_i++) {
            if (brolist[brolist_i].status == 0) {  // 当好友不在线时
                //在线好友`brolist_j`个，保存在bro_list结构体数组的0 - brolist_j-1位
                //故离线好友的下标应从brolist_j开始
                bro_list[brolist_j + brolist_k].status = brolist[brolist_j + brolist_k].status; // 复制状态
                strcpy(bro_list[brolist_j + brolist_k].uid, brolist[brolist_j + brolist_k].uid); // 复制uid
                brolist_k++;  //离线好友数
                printf("\033[%d;42H%d. %s", brolist_j + 13 + brolist_k, brolist_k + brolist_j, brolist[brolist_i].uid);
            }
        }
    }
    printf("\033[30;66H"); // 刷新后需要重新定位光标

    return 0;
}

void qq_show()
{
  system("clear");
  // printf("                                              \n");
  printf("\n\n\n\n\n\n");
  printf("\033[31m                                                             "
         "1100000000011                 \n");
  usleep(50000);
  printf("\033[32m                                                           "
         "00000000000000000               \n");
  usleep(50000);
  printf("\033[33m                                                         "
         "000000000000000000001             \n");
  usleep(50000);
  printf("\033[34m                                                        "
         "00000011000000110000000            \n");
  usleep(50000);
  printf("\033[35m                                                       "
         "000000    1001    0000000           \n");
  usleep(50000);
  printf("\033[36m                                                      000000 "
         "  11 00  1   0000000          \n");
  usleep(50000);
  printf("\033[37m                                                     1000000 "
         " 101 00 001  00000001         \n");
  usleep(50000);
  printf("\033[38m                                                     "
         "00000000    1001    000000000         \n");
  usleep(50000);
  printf("\033[39m                                                     "
         "00000000001000000110000000000         \n");
  usleep(50000);
  printf("\033[30m                                                     "
         "00000001111111111111100000000         \n");
  usleep(50000);
  printf("\033[31m                                                     00001   "
         "                00000         \n");
  usleep(50000);
  printf("\033[32m                                                     000001  "
         "111     1111  1100000         \n");
  usleep(50000);
  printf("\033[33m                                                    "
         "1011000000000000000000000001101        \n");
  usleep(50000);
  printf("\033[34m                                                    "
         "101111000000000000000000011110         \n");
  usleep(50000);
  printf("\033[35m                                                   "
         "000111111111111000111111111010001       \n");
  usleep(50000);
  printf("\033[36m                                                  "
         "00000100111111111111111010000000001      \n");
  usleep(50000);
  printf("\033[37m                                                 000001  "
         "10001110000000000001   000001     \n");
  usleep(50000);
  printf("\033[38m                                                000000   "
         "101100011111111         000001    \n");
  usleep(50000);
  printf("\033[39m                                               1000000   "
         "0011101                 000000    \n");
  usleep(50000);
  printf("\033[30m                                               0000 00  "
         "0001100                 10010001   \n");
  usleep(50000);
  printf("\033[31m                                              1000  100 "
         "1000000                 00   000   \n");
  usleep(50000);
  printf("\033[32m                                              11     100  "
         "1100                 00     11   \n");
  usleep(50000);
  printf("\033[33m                                                       00    "
         "                100           \n");
  usleep(50000);
  printf("\033[34m                                                     1111001 "
         "              1001111         \n");
  usleep(50000);
  printf("\033[35m                                                    "
         "0111111000011111111100001111111        \n");
  usleep(50000);
  printf("\033[36m                                                    "
         "1111111111100111100011111111101        \n");
  usleep(50000);
  printf("\033[37m                                                     "
         "11111111111       1111111111          \n");
  usleep(2000000);

  printf("\n");

  return;
}

/*void qq_show2(){
system("clear");

        printf("                                              \n");
        printf("\033[34m");
        printf("\n\n\n");
        printf(" 1111                           \n"); printf(" 10010000 \n");
    printf(" 1001 100001                       \n"); printf(" 11    10000 \n");
    printf(" 111  1001                       \n"); printf(" 1111 \n"); printf("
0110000                          \n"); printf(" 0000000001 \n"); printf("
1001100000000001                       \n"); printf(" 1000 100000000  001 \n");
    printf(" 10000 000000001 00001                    \n"); printf("
1000001001100001 00000                    \n"); printf(" 1000000100100000
1000001                   \n"); printf(" 10000000100000000 1000000 \n");
    printf("                                                            000000
10100000000 1000000                   \n"); printf(" 000001  10100000000 1000000
\n"); printf("                                                          00001
10100000000100000001                  \n"); printf(" 100001 110000000000000000
\n"); printf("                                                         0001 1111
10000000000000001                   \n"); printf(" 000  111111011000000000000001
\n"); printf(" 10011111111100000000001100000                     \n");

        usleep(500000);
}*/

void qq_show1() {
  system("clear");
  printf("\033[34m");
  printf("                                     \n\n\n\n\n\n");
  printf("                                                                     "
         " 100000001                              \n");
  printf("                                                                     "
         "000011100010                             \n");
  printf("                                                                    "
         "10001011000011                           \n");
  printf("                                                                    "
         "111111111000001                           \n");
  printf("                                                                     "
         "1011110110000                           \n");
  printf("                                                                     "
         " 111110111101                            \n");
  printf("                                                                     "
         "000111111111                              \n");
  printf("                                                                     "
         "00011100000                               \n");
  printf("                                                                    "
         "10000000001                              \n");
  printf("                                                                11 "
         "00000000000000                            \n");
  printf("                                                             10001 "
         "00000000100001                            \n");
  printf("                                                            00000  "
         "000000000000 1000                         \n");
  printf("                                                           000000 "
         "101000100000 100000                        \n");
  printf("                                                          1000000 "
         "100001000001 0000000                       \n");
  printf("                                                         "
         "000000001000110000001 00000001                      \n");
  printf("                                                        "
         "0000000001000110000001 00000000                      \n");
  printf("                                                       "
         "1000000000100000000000  00000000                      \n");
  printf("                                                     100000000000 "
         "00000000000  000000001                     \n");
  printf("                                                    100000001  00 "
         "00000000000 1000000000                     \n");
  printf("                                                    0000000    00 "
         "00000000000 1000000000                     \n");
  printf("                                                  100000011    00 "
         "00000000000 1000000000                     \n");
  printf("                                                  000000       10 "
         "0000000000000000000000                     \n");
  printf("                                                 000000011111   "
         "110000000000000000000000                     \n");
  printf("                                                100001  1111101  "
         "1000000000000000000000                      \n");
  printf("                                               10001     "
         "11111111100000000000000000000                       \n");
  printf("                                               "
         "00001111111111100010000000000100000000                        \n");
  printf("                                               "
         "01111111111111110000000000001 00000001                        \n");

  usleep(1000000);
}
